#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Author  : Leo.W
# @Date    : 2024/1/26 9:48
# @File    : 8-data_structure.py
# @Software: PyCharm

"""
Python中常见的数据结构可以统称为 容器（container）。而 序列（list, tuple 和 range）、映射（字典 dict）以及 集合（set）是三类主要的容器：
序列（list, tuple 和 range）：
1、列表（list）中的元素是有序的，元素内容可以修改。列表就像银行办业务时，排队取号一样，每个人有一个序号，通过叫号，可以找到这个人。
2、元组（tuple）中的元素也是有序的，但元素内容不可修改，元组和列表不同的地方时，元组中的元素不可以更改。
3、range 类型表示不可变的数字序列，通常用于在 for 循环中循环指定的次数。
   相比常规 list 或 tuple 的优势在于一个 range 对象总是占用固定数量的（较小）内存，不论其所表示的范围有多大（因为它只保存了 start, stop 和 step 值，并会根据需要计算具体单项或子范围的值）。
4、字符串（str）：文本序列类型
5、二进制序列类型 -- bytes, bytearray, memoryview

映射（字典 dict）：
1、字典（dict)中的元素无序，内容可以修改，字典和我们常见的字典概念相似，通过关键字产找值，关键字和值之间产生一种映射关系，通过关键字可以找到对应的值，以key-value键值对存储.

集合（set 和 frozenset）：
1、set：中的元素是无序且唯一的，且是可变的，其内容可以使用 add() 和 remove() 这样的方法来改变。
2、frozenset：类型是不可变并且为 hashable --- 其内容在被创建后不能再改变；因此它可以被用作字典的键或其他集合的元素。
"""
import collections
from itertools import zip_longest

"""
序列类型 --- list, tuple, range
通用序列操作:
x in s			        如果 s 中的某项等于 x 则结果为 True，否则为 False
x not in s		        如果 s 中的某项等于 x 则结果为 False，否则为 True
s + t			        s 与 t 相拼接
s * n 或 n * s	        相当于 s 与自身进行 n 次拼接
s[i]			        s 的第 i 项，起始为 0
s[i:j]			        s 从 i 到 j 的切片,不包含 s[j]
s[i:j:k]		        s 从 i 到 j 步长为 k 的切片,不包含 s[j]
len(s)			        s 的长度
min(s)			        s 的最小项
max(s)			        s 的最大项
s.count(x)		        x 在 s 中出现的总次数
s.index(x[, i[, j]])	x 在 s 中首次出现项的索引号（索引号在 i 或其后且在 j 之前）

序列的遍历：
1. 使用 for...in 语句循环遍历
2. 使用 enumerate(s1) 函数可以同时取出位置索引和对应的值，进行遍历： for i, v in enumerate(s1):
"""


# ----------------------------- 列表（list）----------------------------- #
"""
列表是可变序列，通常用于存放同类项目的集合（其中精确的相似程度将根据应用而变化）。
class list([iterable]) : 
构造器将构造一个列表，其中的项与 iterable 中的项具有相同的的值与顺序。
iterable 可以是序列、支持迭代的容器或其它可迭代对象。 
 - 如果 iterable 已经是一个列表，将创建并返回其副本，类似于 iterable[:]。 例如，list('abc') 返回 ['a', 'b', 'c'] 而 list( (1, 2, 3) ) 返回 [1, 2, 3]。 
 - 如果没有给出参数，构造器将创建一个空列表 []。

可以用多种方式构建列表：
 - 使用一对方括号来表示空列表: []
 - 使用方括号，其中的项以逗号分隔: [a], [a, b, c]
 - 使用列表推导式: [x for x in iterable]
 - 使用类型的构造器: list() 或 list(iterable)
 
list 常用方法：
 - list.append(x)：在列表末尾添加一个元素，相当于 a[len(a):] = [x] 。
 - list.extend(iterable)：用可迭代对象的元素扩展列表。相当于 a[len(a):] = iterable 。
 - list.insert(i, x)：在指定位置i插入元素x。第一个参数是插入元素的索引，因此，a.insert(0, x) 在列表开头插入元素， a.insert(len(a), x) 等同于 a.append(x) 。
 - list.remove(x)：从列表中删除第一个值为 x 的元素。未找到指定元素时，触发 ValueError 异常。
 - list.pop([i])：删除列表中指定位置的元素，并返回被删除的元素。未指定位置时，a.pop() 删除并返回列表的最后一个元素。（方法签名中 i 两边的方括号表示该参数是可选的，不是要求输入方括号。这种表示法常见于 Python 参考库）。
 - list.clear():删除列表里的所有元素，相当于 del a[:] 。
 - list.index(x[, start[, end]]):返回列表中第一个值为 x 的元素的零基索引。未找到指定元素时，触发 ValueError 异常。
 -     可选参数 start 和 end 是切片符号，用于将搜索限制为列表的特定子序列。返回的索引是相对于整个序列的开始计算的，而不是 start 参数。
 - list.count(x):返回列表中元素 x 出现的次数。
 - list.sort(*, key=None, reverse=False):就地排序列表中的元素（要了解自定义排序参数，详见 sorted()）。
 - list.reverse():翻转列表中的元素。
 - list.copy():返回列表的浅拷贝。相当于 a[:] 。
"""


def list_test():
    """
    列表（list）:
    在内存空间中是连续地址，查询速度快，修改也快，但不利于频繁新增或删除元素（需要注意的是，队尾新增或者删除元素并不影响性能）。
    列表是可变的，这是它区别于字符串和元组的最重要的特点，一句话概括即：列表可以修改，而字符串和元组不能。
    :return:
    """
    # 1.创建 list 对象
    # ---------- 1.创建列表 --------- #
    l1 = ["A", "B", "C", 4, 5, 6]  # 创建方式类似于javascript中的数组
    l2 = list("hello")  # 通过list函数（其实list是一种类型而不是函数）对字符串创建列表 ['h', 'e', 'l', 'l', 'o']
    print(l1, l2)
    # 切片：s[i:j] -> s 从 i 到 j 的切片,不包含 s[j]
    print(l1[1:3])  # ['B', 'C']

    # ---------- 2.列表常用方法 --------- #
    l1.append(10)  # 像列表尾部添加一个元素
    l1.extend(("X", "Y", "Z"))  # 扩展列表，将元组插入队尾
    l1.extend([100, 200])  # 扩展列表，将列表插入队尾
    print(l1)  # ['A', 'B', 'C', 4, 5, 6, 10, 'X', 'Y', 'Z', 100, 200]
    # 扩展列表，将字符串拆解成字符列表插入队尾 ['h', 'e', 'l', 'l', 'o', ' ','w', 'o', 'r', 'l', 'd']
    l2.extend(" world")
    print(l2, "count('l')->", l2.count("l"))  # count('l')-> 3

    try:
        """
        list.sort(*, key=None, reverse=False):
        排序列表中的元素，只使用 < 来进行各项间比较。 异常不会被屏蔽 —— 如果有任何比较操作失败，整个排序操作将失败
         - key 指定带有一个参数的函数，用于从每个列表元素中提取比较键 (例如 key=str.lower)。 对应于列表中每一项的键会被计算一次，然后在整个排序过程中使用。
               默认值 None 表示直接对列表项排序而不计算一个单独的键值。
               使用 functools.cmp_to_key() 将(Python 2.x 旧式)比较函数转换为新式的 key 函数。主要用于 Python 2.x 版本的兼容处理
         - reverse 默认为False, 如果设为 True，则每个列表元素将按反向顺序比较进行排序

        同样，可以是用内置的 sorted() 函数进行排序：sorted(iterable, /, *, key=None, reverse=False)

        注意：排序算法只使用 < 在项目之间比较。 虽然定义一个 __lt__() 方法就足以进行排序，但 PEP 8 建议实现所有六个 富比较 方法（rich comparison methods）：
        运算符号与 富比较 方法的对应关系如下：
        object.__lt__(self, other) : x<y  调用 x.__lt__(y)
        object.__le__(self, other) : x<=y 调用 x.__le__(y)
        object.__eq__(self, other) : x==y 调用 x.__eq__(y)
        object.__ne__(self, other) : x!=y 调用 x.__ne__(y)
        object.__gt__(self, other) : x>y  调用 x.__gt__(y)
        object.__ge__(self, other) : x>=y 调用 x.__ge__(y)

        由于所有类型都是 object 的（直接或间接）的子类型，因此它们都从 object 继承了默认的比较行为。
        自定义类型可以通过实现 富比较 方法 如 __lt__() 来自定义它们的比较行为
        """
        l1.sort(key=str)  # 排序，key=str -> 将元素转换成字符后再比较排序
        print(l1)  # [10, 100, 200, 4, 5, 6, 'A', 'B', 'C', 'X', 'Y', 'Z']
        # 使用内置 sorted() 函数排序，返回排序后的新列表,使用 lambda 表达式设置 key 函数
        sl = sorted(l1, key=lambda x: str(x), reverse=True)
        print(sl)  # ['Z', 'Y', 'X', 'C', 'B', 'A', 6, 5, 4, 200, 100, 10]
        # 注意：list 中不是所有数据都可以排序或比较。如下案例中 因为整数不能与字符串进行比较，所以排序时将报错。
        # TypeError: '<' not supported between instances of 'int' and 'str'
        l1.sort()
    except TypeError as ex:
        print("list 排序时发生异常：", ex)

    # 使用list实现栈（“后进先出”）
    stack = [2, 3, 4]
    stack.append(5)  # 入栈
    print(stack.pop())  # 出栈

    # 用列表实现队列（“先进先出”）
    # 列表也可以用作队列，最先加入的元素，最先取出（“先进先出”）；然而，列表作为队列的效率很低。因为，在列表末尾添加和删除元素非常快，但在列表开头插入或移除元素却很慢（因为所有其他元素都必须移动一位）
    # 实现队列最好用 collections.deque
    from collections import deque

    queue = deque(["Eric", "John", "Michael"])
    queue.append("Terry")  # Terry arrives
    queue.append("Graham")  # Graham arrives
    print(queue.popleft(), queue.popleft(), queue.pop())  # Eric John Graham

    # 列表推导式: 对序列或可迭代对象中的每个元素应用某种操作，用生成的结果创建新的列表
    # 格式：[表达式 for x in iterator1 for y in iterator2 ... if 条件表达式]
    # 其中: 表达式为 最终生成的列表元素->可以根据 x,y,.. 由多个 for 语句中获取变量进行表达式计算最终生成元素。
    # 如下代码，相当于：squares = list(map(lambda x: x**2, range(10)))
    squares = [x**2 for x in range(10)]
    print(squares)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    # 如上，列表推导式的 方括号 内包含以下内容：一个表达式，后面为一个 for 子句，然后，是零个或多个 for 或 if 子句。
    # 结果是由表达式依据 for 和 if 子句求值计算而得出一个新列表
    combs = [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y]
    """
    等价于：
    combs = []
    for x in [1,2,3]:
        for y in [3,1,4]:
            if x != y:
                combs.append((x, y))
    注意，上面两段代码中，for 和 if 的顺序相同。
    返回的是一个元组列表：表达式是元组（例如上例的 (x, y)）时，必须加上括号。
    """
    print(combs)  # [(1, 3), (1, 4), (2, 3), (2, 1), (2, 4), (3, 1), (3, 4)]

    # 注意 if 条件位置，与 [x * y for x in range(10) for y in range(20, 30) if x % 2 == 1 and y % 2 == 0] 输出结果相同
    print([x * y for x in range(10) if x % 2 == 1 for y in range(20, 30) if y % 2 == 0])

    # 展开一个二维列表
    vec = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    # 下面列表推导式的语义为：
    # for elem in vec -> 遍历 vec 每个元素赋值给 elem （列表）
    # for num in elem -> 在遍历 elem 获取列表元素 num
    # 表达式 num -> 最终列表元素取值。
    print([num for elem in vec for num in elem])  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

    # 嵌套的列表推导式
    matrix = [
        [1, 2, 3, 4],
        [5, 6, 7, 8],
        [9, 10, 11, 12],
    ]
    # 列表推导式中的初始表达式可以是任何表达式，甚至可以是另一个列表推导式 [row[i] for row in matrix]
    transposed = [[row[i] for row in matrix] for i in range(4)]
    print(transposed)  # [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
    # 相当于
    transposed = []
    for i in range(4):  # 先推导最外层的 [... for i in range(4)]
        # 再带入上下文的 i 推导第二层的 [row[i] for row in matrix]
        transposed.append([row[i] for row in matrix])

    # del 语句：按索引而不是值从列表中移除条目。
    a = [-1, 1, 66.25, 333, 333, 1234.5]
    del a[0]  # 删除列表中第一个元素
    print("del a[0] -> a:", a)  # a: [1, 66.25, 333, 333, 1234.5]
    del a[2:4]  # 删除切片
    print("del a[2:4] -> a:", a)  # a: [1, 66.25, 1234.5]
    del a[:]  # 清空列表
    print("del a[:] -> a:", a)  # a:[]
    del a  # 删除整个变量：此后，再引用 a 就会报错


# list_test()


# ----------------------------- 元组（tuple）----------------------------- #
"""
元组: 不可变序列
通常用于储存异构数据的多项集（例如由 enumerate() 内置函数所产生的二元组）。 元组也被用于需要同构数据的不可变序列的情况（例如允许存储到 set 或 dict 的实例）。

class tuple([iterable])
可以用多种方式构建元组：
- 使用一对圆括号来表示空元组: ()
- 使用一个后缀的逗号来表示单元组: a, 或 (a,)
- 使用以逗号分隔的多个项: a, b, c or (a, b, c)
- 使用内置的 tuple(): tuple() 或 tuple(iterable)

命名元组 collections.namedtuple() ：
命名元组赋予每个位置一个含义，提供可读性和自文档性。它们可以用于任何普通元组，并添加了通过名字获取值的能力，通过索引值也是可以的。
命名元组构造函数：返回一个新的元组子类，名为 typename，这个新的子类用于创建类元组的对象，可以通过字段名来获取属性值，同样也可以通过索引和迭代获取值。
collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)：
 - typename：元祖子类的名称
 - field_names：是一个像 [‘x’, ‘y’] 一样的字符串序列。另外 field_names 可以是一个纯字符串，用空白或逗号分隔开元素名，比如 'x y' 或者 'x, y' 
 - rename： 为True时，无效字段名会自动转换成位置名。比如 ['abc', 'def', 'ghi', 'abc'] 转换成 ['abc', '_1', 'ghi', '_3'] ， 消除关键词 def 和重复字段名 abc 。
 - defaults：默认值，可以为 None 或者是一个默认值的 iterable 。
 - module：设置命名元组的 __module__ 属性值
 
命名元祖示例：
Point = namedtuple('Point', ['x', 'y'])
p = Point(11, y=22)  # Point(x=11, y=22)
"""


def tuple_test():
    # 输入时，圆括号可有可无
    # 元组的元素可以是任意值，甚至是元祖。如：(567, 8)
    t1 = 1, 2, 3, 4, (567, 8), ["A", "B"]
    # 不允许为元组中的单个元素赋值，当然，可以创建含列表等可变对象的元组
    try:
        t1[5].append("C")
        t1[0] = 10  # 'tuple' object does not support item assignment
    except TypeError as ex:
        print(ex)

    print(t1)  # (1, 2, 3, 4, (567, 8), ['A', 'B', 'C']) # 输出时，元组都要由圆括号标注，这样才能正确地解释嵌套元组。

    # 构造 0 个或 1 个元素的元组比较特殊：
    empty = ()  # 用一对空圆括号就可以创建空元组；
    t2 = ("hello",)  # 只有一个元素的元组可以通过在这个元素后添加逗号来构建（圆括号里只有一个值的话不够明确）
    print(empty, "->", len(empty), t2, "->", len(t2))  # () -> 0 ('hello',) -> 1

    # 序列解包时，左侧变量与右侧序列元素的数量应相等。
    x, y, z = (123, "ABC", (456, "678"))
    print("x=", x, "y=", y, "z=", z)  # x= 123 y= ABC z= (456, '678')
    # 由上可知：多重赋值其实只是元组打包和序列解包的组合。
    a, b = "A", "B"  # 等同于 a, b = ("A", "B")

    # 命名元组
    Person = collections.namedtuple("Person", ["name", "age", "gender"])
    p = Person("张三", 18, "男")
    print(p)  # Person(name='张三', age=18, gender='男')

    # 元组的一般操作
    t3 = ("a", "b", "c")
    t4 = tuple(x.upper() for x in t3)  # 序列推导式创建元祖
    # s + t -> s 与 t 相拼接
    t5 = t3 + t4
    print("t3 + t4 =", t5)  # t3 + t4 = ('a', 'b', 'c', 'A', 'B', 'C')
    # s * n 或 n * s -> 相当于 s 与自身进行 n 次拼接
    print("t3 * 2 =", t3 * 2)  # t3 * 2 = ('a', 'b', 'c', 'a', 'b', 'c')
    # s[i:j] -> 切片 s 从 i 到 j 的切片(不包含s[j])
    print("t5[3:5] =", t5[3:5])  # t5[3:5] = ('A', 'B')
    # min(s) -> s 的最小项，max(s) -> s 的最大项
    print("min(t5) = ", min(t5), "max(t5) = ", max(t5))  # min(t5) =  A max(t5) =  c
    # s.count(x) -> x 在 s 中出现的总次数
    print("t3.count('b') = ", t3.count("b"))  # t3.count('b') =  1
    # s.index(x[, i[, j]]) -> x 在 s 中首次出现项的索引号（索引号在 i 或其后且在 j 之前）
    print(t5.index("A"))  # 3


# tuple_test()


# ----------------------------- 集合（set）----------------------------- #
"""
集合是由【不重复】元素组成的【无序】容器。 
相应地，集合不支持索引、切片或其他序列类的操作。
基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算。
目前有两种内置集合类型:
class set([iterable]) : 类型是【可变】的，其内容可以使用 add() 和 remove() 这样的方法来改变。 由于是可变类型，它没有哈希值，且不能被用作字典的键或其他集合的元素。 
class frozenset([iterable]) : 类型是【不可变】并且为 hashable，其内容在被创建后不能再改变；因此它可以被用作字典的键或其他集合的元素。

由于集合中元素【不重复】的特性可以使用 set() 去除序列中的重复元素。
由于集合中元素【无序】的特性可以使用 sorted()函数对集合元素进行排序。
"""


def set_test():
    # ---------- 1.创建集合 --------- #
    # class set([iterable])
    # class frozenset([iterable])
    # 用花括号或 set() 函数。注意，创建空集合只能用 set()，不能用 {}，{} 创建的是空字典
    # 使用花括号内以逗号分隔元素的方式:
    s1 = {1, 2, "A", "A", True, ("hello", "world")}
    # 使用类型构造器:set(), set('foobar'), set(['a', 'b', 'foo'])
    s2 = set("abcdefg")  # {'d', 'a', 'c', 'b', 'f', 'g', 'e'}
    s2.add("a")  # 添加已存在的元素不会报错，但是操作无效
    print(s1, s2)  # 注意，每次输出集合元素次序可能都不一样，由此可见集合是无序的

    # 使用集合推导式:  与 列表推导式 类似，集合也支持推导式：
    s3 = {x for x in range(1, 11) if x % 2 == 0}  # 注意是 {} 括住
    print(s3)  # {8, 2, 4, 6 , 10}
    print("A" in s1)  # True

    # ---------- 2.集合的运算 --------- #
    # 使用运算符进行两个集合的运算
    a = set("abracadabra")  # 字符串转换成字符集合会自去重  {'r', 'c', 'a', 'b', 'd'}
    b = set("alacazam")  # {'c', 'm', 'a', 'z', 'l'}
    print(a, b)
    # print(a + b)  # 不支持 + 运算符
    # a - b 差集: letters in a but not in b
    print(a - b)  # {'r', 'd', 'b'}
    # a & b 交集：letters in both a and b
    print(a & b)  # {'a', 'c'}
    # a | b 合集：letters in a or b or both
    print(a | b)  # {'r', 'z', 'd', 'c', 'm', 'a', 'b', 'l'}
    # a ^ b 对称差分：letters in a or b but not both
    print(a ^ b)  # {'m', 'r', 'z', 'd', 'b', 'l'}

    # ---------- 3.遍历集合元素 --------- #
    # 排序并遍历集合中元素
    for x in sorted(s1, key=str):
        print(x)
    # 用 enumerate() 函数可以同时取出位置索引和对应的值：
    # 注意：由于set是无序的，所以每次 i 对应的 value 可能不一样
    for i, v in enumerate(s1):
        print(i, v)


# set_test()


# ----------------------------- 映射类型（dict）----------------------------- #
"""
mapping -- 映射：
一种支持任意键查找并实现了 collections.abc.Mapping 或 collections.abc.MutableMapping 抽象基类 所规定方法的容器对象。
此类对象的例子包括：
 - dict：字典，存储 键:值 对
 - collections.defaultdict：dict 的子类，它重写了一个方法并添加了一个可写的实例变量。 其余的功能与 dict 类相同
 - collections.OrderedDict：dict 的子类，有序词典就像常规词典一样，旨在擅长重新排序操作。 空间效率、迭代速度和更新操作的性能是次要的。
 - collections.Counter：dict 的子类，一个计数器工具，为的是可以方便快速地计账。value 存储的是 key 的计数值。计数可以是任何整数，包括零或负的计数值。如：Counter(a=4, b=2, c=0, d=-2)

字典的【键】几乎 可以为任何值：不是 hashable 的值，即包含列表、字典或其他可变类型（按值比较而非按对象标识比较）的值不可被用作键。 
class dict(**kwargs)
class dict(mapping, **kwargs)
class dict(iterable, **kwargs)
字典可用多种方式来创建:
 - 使用花括号内以逗号分隔 键:值 对的方式: {'jack': 4098, 'sjoerd': 4127} or {4098: 'jack', 4127: 'sjoerd'}
 - 使用字典推导式: {}, {x:x ** 2 for x in range(10)}
 - 使用类型构造器: dict(), dict([('foo', 100), ('bar', 200)]), dict(foo=100, bar=200)
"""


def dic_test():
    # ---------- 1.创建字典对象 --------- #
    a = dict(one=1, two=2, three=3)  # 使用命名参数创建字典
    b = {"one": 1, "two": 2, "three": 3}  # 使用 {"key":value} 的形式创建字典
    # 使用 zip([key列表],[value列表]) 函数进行 key-value 配对后创建字典
    c = dict(zip(["one", "two", "three"], [1, 2, 3]))
    d = dict([("two", 2), ("one", 1), ("three", 3)])  # 使用(key,value)形式的元祖列表创建字典
    e = dict({"three": 3, "one": 1, "two": 2})
    f = dict({"one": 1, "three": 3}, two=2)
    print(a == b == c == d == e == f)  # True

    # classmethod fromkeys(iterable[, value]): 使用来自 iterable 的键创建一个新字典，并将键值设为 value。
    g = dict.fromkeys(["one", "two", "three"], 0)
    print(g)  # {'one': 0, 'two': 0, 'three': 0}

    # 字典推导式可以用任意键值表达式创建字典：
    h = {x: x * x for x in range(1, 5)}
    print(h)  # {1: 1, 2: 4, 3: 9, 4: 16}

    # ---------- 2.遍历字典 --------- #
    # 字典视图对象 :dict.keys(), dict.values() 和 dict.items() 所返回的对象是 视图对象。
    # 视图对象提供字典条目的一个动态视图，这意味着当字典改变时，视图也会相应改变。
    dks = d.keys()
    dvs = d.values()
    dkvs = d.items()
    # setdefault(key, default=None) :如果字典存在键 key ，返回它的值。如果不存在，插入值为 default 的键 key ，并返回 default 。 default 默认为 None。
    d.setdefault("A", 100)  # 字典视图对象将发生变化
    print(dks)  # dict_keys(['two', 'one', 'three', 'A'])
    print(dvs)  # dict_values([2, 1, 3, 100])
    print(dkvs)  # dict_items([('two', 2), ('one', 1), ('three', 3), ('A', 100)])

    # d.items() 返回格式为 (key,value) 的元祖列表。
    for k, v in d.items():  # 这里使用了对元祖的解包操作 k,v = (key,value)
        print(f"key:{k},value:{v}")

    # 用 enumerate() 函数可以同时取出位置索引和对应的值：
    for i, k in enumerate(d):
        print(i, k)

    # iter(d) :返回以字典的键为元素的迭代器。 这是 iter(d.keys()) 的快捷方式。
    for key in iter(d):
        print(key)

    # ---------- 3.字典常用操作 --------- #
    # len(d) -> 返回字典 d 中的项数
    # list(d) -> 返回字典 d 中使用的所有键的列表
    # len(d) =  3 list(d) =  ['two', 'one', 'three']
    print("len(d) = ", len(d), "list(d) = ", list(d))

    # key in d, key not in d
    print(("A" in d), ("A" not in d))  # False True

    # pop(key[, default]) : 如果 key 存在于字典中则将其移除并返回其值，否则返回 default。 如果 default 未给出且 key 不存在于字典中，则会引发 KeyError。
    # popitem() : 从字典中移除并返回一个 (键, 值) 对。 键值对会按 LIFO 的顺序被返回。
    # d.pop("one") =  1 , d = {'two': 2, 'three': 3}
    print('d.pop("one") = ', d.pop("one"), ", d = ", d)

    # update([other]) ：使用来自 other 的键/值对更新字典，覆盖原有的键,key 不存在则添加。 返回 None。
    d.update({"one": 10, "two": 20})  # 参数是字典
    d.update(three=30, five=500)  # 命名参数形式
    print(d)  # {'two': 20, 'three': 30, 'A': 100, 'one': 10, 'five': 500}
    # clear() :移除字典中的所有元素。
    # copy() :返回原字典的浅拷贝。

    # d | other ：合并 d 和 other 中的键和值来创建一个新的字典，两者必须都是字典。当 d 和 other 有相同键时， other 的值优先。
    # d |= other ：用 other 的键和值更新字典 d ，other 可以是 mapping 或 iterable 的键值对。当 d 和 other 有相同键时， other 的值优先。
    d1 = a | {"one": 100, "A": "abc"}
    print(d1)  # {'one': 100, 'two': 2, 'three': 3, 'A': 'abc'}


# dic_test()

"""
内置函数：zip(*iterables, strict=False)
用于遍历多个序列，zip() 返回元组的迭代器，其中第 i 个元组包含的是每个参数迭代器的第 i 个元素。
zip() 会把行变成列，把列变成行。似于 矩阵转置 。
zip() 是延迟执行的：直至迭代时才会对元素进行处理，比如 for 循环或放入 list 中。
"""


def zip_test():
    questions = ["name", "quest", "favorite color"]
    answers = ["lancelot", "the holy grail", "blue"]
    for q, a in zip(questions, answers):
        print("What is your {0}?  It is {1}.".format(q, a))

    t1 = (1, 2, 3)
    t2 = ("A", "B", "C", "D")
    t3 = ("壹", "贰", "叁", "肆", "伍")

    # 1.默认情况下（strict=False），zip() 在最短的迭代完成后停止。较长可迭代对象中的剩余项将被忽略，结果会裁切至最短可迭代对象的长度：
    for x in zip(t1, t2, t3):
        print(x)

    """
    输出：
    (1, 'A', '壹')
    (2, 'B', '贰')
    (3, 'C', '叁')
    """

    # 2.通常 zip() 用于可迭代对象等长的情况,当设置命名参数 strict=True 时，如果遍历的对象长度不同，则引发 ValueError 异常
    try:
        for x in zip(t1, t2, t3, strict=True):
            print(x)
    except ValueError as error:
        print(error)  # zip() argument 2 is longer than argument 1

    # 3.与上面情况相反，如果想遍历完最长的对象，而长度较短的可用常量进行填充，使用 itertools.zip_longest() 来完成
    #   fillvalue 参数用于设置填充缺失值
    for x in zip_longest(t1, t2, t3, fillvalue="-"):
        print(x)
    """
    输出：
    (1, 'A', '壹')
    (2, 'B', '贰')
    (3, 'C', '叁')
    ('-', 'D', '肆')
    ('-', '-', '伍')
    """


# zip_test()
